package org.yoki.edu.bg.radius.filter.app.obs;

import lombok.Getter;
import lombok.Setter;

import java.util.Vector;
import java.util.concurrent.*;

/**
 * 参考java.util.Observable
 *
 * @author Sky$
 * @Description: TODO
 * @date 2018/1/4$ 18:28$
 */
public class SynObservable<T extends SynObserver> {

    protected ThreadPoolExecutor executor;
    @Setter
    @Getter
    private int corePollSize = 20;
    @Setter
    @Getter
    private int maximumPoolSize = 40;
    @Setter
    @Getter
    private int keepAliveTime = 100;
    @Setter
    @Getter
    private BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(40);

    private boolean changed = false;
    private Vector<T> obs =  new Vector();


    public SynObservable() {
        obs = new Vector<>();
    }

    public synchronized void addObserver(T o) {
        if (o == null) {
            throw new NullPointerException();
        }
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    public synchronized void deleteObserver(T o) {
        obs.removeElement(o);
    }

    public void notifyObservers() {
        notifyObservers(null);
    }

    public void notifyObservers(Object... args) {
        if (null == executor) {
            synchronized (this) {
                if (null == executor) {
                    /*ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
                            .setNameFormat("abstractPacketTransit-pool-%d").build();*/
                    executor = new ThreadPoolExecutor(this.corePollSize, this.maximumPoolSize, this.keepAliveTime,
                            TimeUnit.MILLISECONDS, this.workQueue,
                            new ThreadPoolExecutor.AbortPolicy());
                }
            }
        }
        Object[] var2;
        synchronized (this) {
            if (!this.changed) {
                return;
            }
            var2 = obs.toArray();
            this.clearChanged();
        }
        MyTask myTask = new MyTask(this, var2, args);
        executor.execute(myTask);
    }

    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

    protected synchronized void setChanged() {
        changed = true;
    }

    protected synchronized void clearChanged() {
        changed = false;
    }

    public synchronized boolean hasChanged() {
        return changed;
    }

    public synchronized int countObservers() {
        return obs.size();
    }

    class MyTask implements Runnable {

        private Object[] arrLocal;
        private SynObservable observable;
        private Object[] args;

        public MyTask(SynObservable observable, Object[] arrLocal, Object... args) {
            this.observable = observable;
            this.arrLocal = arrLocal;
            this.args = args;
        }

        @Override
        public void run() {
            for (int i = arrLocal.length - 1; i >= 0; i--) {
                ((SynObserver) arrLocal[i]).update(observable, args);
            }
        }
    }

}
